////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_QUATERNION_H__
#define __WN_MATH_QUATERNION_H__

#include "WNCore/inc/WNTypes.h"
#include "WNCore/inc/WNTypeTraits.h"
#include "WNCore/inc/WNAssert.h"
#include "WNMath/inc/WNConfig.h"

namespace WNMath {
    namespace __WNInternal {
        template <typename Type>
        struct __WNQuaternionBase {
            Type mX;
            Type mY;
            Type mZ;
            Type mW;
        };
    }

    template <typename Type> class WNMatrix3;
    template <typename Type> class WNMatrix4;
    template <typename Type> class WNVector4;

    template <typename Type>
    class WNQuaternion : public __WNInternal::__WNQuaternionBase<Type> {
        WN_STATIC_ASSERT_DESC(WNCore::WNIsReal<Type>::Value == WN_TRUE, "Quaternions of non real number types don't make sense.  Please use one of the available real number types.");

    public:
        WNQuaternion() {}
        WNQuaternion(const WNQuaternion& _quaternion);
        explicit WNQuaternion(Type _number);
        explicit WNQuaternion(const Type* _number);
        explicit WNQuaternion(Type _x, Type _y, Type _z, Type _w);

        operator Type* ();
        operator const Type* () const;

        WNQuaternion& operator += (const WNQuaternion& _quaternion);
        WNQuaternion& operator -= (const WNQuaternion& _quaternion);
        WNQuaternion& operator *= (const WNQuaternion& _quaternion);
        WNQuaternion& operator *= (Type _number);
        WNQuaternion& operator /= (Type _number);

        WNQuaternion operator + () const;
        WNQuaternion operator - () const;

        WNQuaternion operator + (const WNQuaternion& _quaternion) const;
        WNQuaternion operator - (const WNQuaternion& _quaternion) const;
        WNQuaternion operator * (const WNQuaternion& _quaternion) const;
        WNQuaternion operator * (Type _number) const;
        WNQuaternion operator / (Type _number) const;

        WN_BOOL operator == (const WNQuaternion& _quaternion) const;
        WN_BOOL operator != (const WNQuaternion& _quaternion) const;

        WN_VOID Zero();
        WN_VOID Identity();
        WN_VOID Normalize();
        WN_VOID Invert();
        WN_VOID Logarithm();
        WN_VOID Exponent();
        WN_VOID Rotate(const WNQuaternion& _rotation);

        Type Length() const;
        Type LengthSquared() const;

        Type Dot(const WNQuaternion& _quaternion) const;

        WN_BOOL IsZero() const;
        WN_BOOL IsIdentity() const;

        WN_VOID Set(const WNQuaternion& _quaternion);
        WN_VOID Set(Type _number);
        WN_VOID Set(const Type* _number);
        WN_VOID Set(Type _x, Type _y, Type _z, Type _w);

        WNQuaternion GetNormalized() const;
        WNQuaternion GetInverted() const;
        WNQuaternion GetConjugate() const;
        WNQuaternion GetLogarithm() const;
        WNQuaternion GetExponent() const;

        WNMatrix4<Type> ToMatrix44() const;

        WN_VOID Prefetch() const;

        template <typename NewType>
        WNQuaternion<NewType> ConvertTo() const;
    };

    template <typename Type> WNQuaternion<Type> operator * (Type _number, const WNQuaternion<Type>& _quaternion);
    template <typename Type> WNQuaternion<Type> operator / (Type _number, const WNQuaternion<Type>& _quaternion);
}

#include "WNMath/inc/Internal/WNQuaternion.inl"

#endif // __WN_MATH_QUATERNION_H__